import { Middleware } from 'koa';

import { RouterPlus } from '../router';
import 'reflect-metadata';
import type { RequestMethod } from '../types';

const handlePath = (path: string | undefined): string => {
  if (!path || path === '/') {
    return '';
  }
  if (!path.startsWith('/')) {
    if (path === '/') {
      path = '';
    } else {
      path = '/' + path;
    }
  }
  if (path.length > 1 && path.endsWith('/')) {
    path = path.slice(0, -1);
  }
  path = path.replace(/ /g, '-');
  return path;
};

/**
 * @func methodFactory
 * @desc 请求方法装饰器工厂
 * @param method 请求方法
 */
const methodFactory = (method: RequestMethod) => {
  return (path?: string): MethodDecorator => {
    return (target: any, methodIdentifier: string | symbol, _: PropertyDescriptor) => {
      Reflect.defineMetadata('path', path, target, methodIdentifier);
      Reflect.defineMetadata('methodType', method, target, methodIdentifier);
    };
  };
};

const Get = methodFactory('get');
const Post = methodFactory('post');
const Put = methodFactory('put');
const Delete = methodFactory('delete');
const Patch = methodFactory('patch');
const Head = methodFactory('head');

/**
 * @func Controller
 * @desc 控制器装饰器
 * @param basePath 每个路由模块的基础路径，例如new Router({ prefix: '/users'}) 直接替换成 @Controller('/users')
 */
const Controller = (basePath?: string): ClassDecorator => {
  basePath = handlePath(basePath);

  return (targetClass: any) => {
    // 1.获取原型上的所有方法
    Reflect.ownKeys(targetClass.prototype).forEach((methodIdentifier) => {
      if (methodIdentifier !== 'constructor') {
        // 2.获取方法的方法体
        const methodDescriptor: (...args: any[]) => any = targetClass.prototype[methodIdentifier];

        const middlewares: Middleware[] =
          Reflect.getMetadata('middlewares', targetClass.prototype, methodIdentifier) || [];

        const subPath: string | undefined = Reflect.getMetadata(
          'path',
          targetClass.prototype,
          methodIdentifier,
        );
        const requestMethod: RequestMethod | undefined = Reflect.getMetadata(
          'methodType',
          targetClass.prototype,
          methodIdentifier,
        );

        const fullPath = `${handlePath(basePath)}${handlePath(subPath)}`;

        // 3.获取请求路径，请求类型，和根路由
        /**
         * 注意项目的执行顺序
         * 1.ts-node 执行 main.ts
         * 2.main.ts 执行 initApp, 注册中间件时会执行到 Router.init(app)
         * 3.执行Router.init(app)，此时Router.rootRouter为已经初始化
         * 4.Router.init中执行loadControllers，遍历controllers目录的文件
         * 然后require controllers下的所有controller文件，
         * 让每个controller中的装饰器都被执行，装饰器被执行时将注册全部路由
         */
        if (fullPath && requestMethod) {
          const rootRouter = RouterPlus.rootRouter;
          rootRouter[requestMethod](fullPath, ...middlewares, methodDescriptor);
        }
      }
    });
  };
};

export { Get, Post, Put, Delete, Patch, Head, Controller };
